package com.yp.infrastructure.axon.service;

import com.yp.infrastructure.axon.constants.EnumErrorMessage;
import com.yp.infrastructure.axon.exception.AxonException;
import com.yp.infrastructure.axon.model.BaseAxonCommand;
import com.yp.infrastructure.axon.model.BaseAxonQuery;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
import org.axonframework.messaging.MetaData;
import org.axonframework.queryhandling.QueryGateway;
import org.axonframework.queryhandling.SubscriptionQueryResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import java.util.concurrent.CompletableFuture;

@Slf4j
public abstract class BaseAxonService {


    protected final CommandGateway commandGateway;

    protected final QueryGateway queryGateway;

    public BaseAxonService(@Lazy @Autowired CommandGateway commandGateway, @Lazy @Autowired QueryGateway queryGateway) {
        this.commandGateway = commandGateway;
        this.queryGateway = queryGateway;
    }


    protected <Q extends BaseAxonQuery, R> CompletableFuture<R> query(Class<R> clazz, Q query) throws AxonException {
        return queryGateway.query(query, clazz).exceptionally(this::handlerError);
    }

    protected <Q extends BaseAxonQuery, I, U> SubscriptionQueryResult<I, U> subscribe(Q query, Class<I> clazzInit, Class<U> clazzUpdate) {
        return queryGateway.subscriptionQuery(query, clazzInit, clazzUpdate);
    }

    protected <C extends BaseAxonCommand, R> CompletableFuture<R> send(C command, MetaData metaData) throws AxonException {
        return (CompletableFuture<R>) commandGateway.send(command, metaData).exceptionally(this::handlerError);
    }

    protected <C extends BaseAxonCommand, R> R sendAndWait(C command) throws AxonException {
        try {
            return commandGateway.sendAndWait(command);
        } catch (Throwable throwable) {
            this.handlerError(throwable);
        }
        return null;
    }

    private <E> E handlerError(Throwable throwable) {
        log.debug(throwable.getMessage());
        throw new AxonException(EnumErrorMessage.AXON_ERROR, throwable);
    }


}
